#include "MemoryPool.h"

#include "LogManagement.h"

namespace EngineLib {

	MemoryPool::MemoryPool(void)
	{
		LogManagement::Error(ERROR_CODE_ILLEGAL_CALL);
	}

	MemoryPool::~MemoryPool(void) 
	{}

	unsigned int MemoryPool::DataSize(void) const
	{
		return _chunkCount * _chunkSize;
	}

	unsigned int MemoryPool::StorageSize(void) const
	{
		return sizeof(MemoryPool) + _chunkCount * _chunkSize;
	}

	bool MemoryPool::IsOutOfRange(void *target)
	{
		return MEM_LT(target, GetFirstChunk()) || MEM_GT(target, GetFirstChunk() + _chunkCount * _chunkSize);
	}

	void MemoryPool::InitFreeChunk(void)
	{
		unsigned int count = _chunkCount;
		_freeChunk = GetFirstChunk();
		unsigned char *nextChunk = 0;
		while (count--) {
			if (!count) {
				MemoryPool::SetChunkNext(_freeChunk, 0);
				//_freeChunk = 0;
			}
			else {
				nextChunk = _freeChunk + _chunkSize;
				MemoryPool::SetChunkNext(_freeChunk, nextChunk);
				_freeChunk = nextChunk;
			}
		}
		_freeChunk = GetFirstChunk();
	}
/*
	void MemoryPool::SortFreeChunk(void) {
		if (!_freeChunk)
			return;
		MemoryPoolChunk<T> *cur = _freeChunk->nextFreeChunk;
		while (cur) {
			MemoryPoolChunk<T> *temp = cur->prevFreeChunk;
			while (temp) {
				if (MEM_GT(cur, temp->prevFreeChunk) && MEM_LT(cur, temp)) {
					if (cur->nextFreeChunk) {
						cur->nextFreeChunk->prevFreeChunk = cur->prevFreeChunk;
					}
					cur->prevFreeChunk->nextFreeChunk = cur->nextFreeChunk;
					cur->prevFreeChunk = temp->prevFreeChunk;
					cur->nextFreeChunk = temp;
					if (temp->prevFreeChunk) {
						temp->prevFreeChunk->nextFreeChunk = cur;
					}
					temp->prevFreeChunk = cur;
					break;
				}
				else {
					temp = temp->prevFreeChunk;
				}
			}
			assert(!!temp);
			cur = cur->nextFreeChunk;
		}
	}
*/
	void* MemoryPool::Allocate(void)
	{
		if (!_freeChunk) {
			LogManagement::Error(ERROR_CODE_POOL_OVERFLOW);
			return NULL;
		}
		void *rst = _freeChunk;
		_freeChunk = MemoryPool::GetChunkNext(_freeChunk);
		LogManagement::Log("allocate 128", LOG_CHANNEL_7);
		return rst;
	}

	void MemoryPool::Free(void *data)
	{
		assert(!IsOutOfRange(data));
		assert((((unsigned int)data - (unsigned int)this) - sizeof(MemoryPool)) % _chunkSize == 0);
		MemoryPool::SetChunkNext(data, _freeChunk);
		_freeChunk = (unsigned char*)data;
		LogManagement::Log("free 128", LOG_CHANNEL_7);
	}

	MemoryPool* MemoryPool::New(MemoryStack *memoryStack, unsigned int chunkCount, unsigned int chunkSize, MemoryStack::Position left_or_right)
	{
		assert(memoryStack && chunkCount);
		chunkSize = chunkSize > 4 ? chunkSize : 4;
		unsigned int totalSize = sizeof(MemoryPool) + chunkCount * chunkSize;
		MemoryPool *rst = 
			(left_or_right == MemoryStack::LEFT) ?
				((MemoryPool*)memoryStack->AllocateLeft(totalSize)) :
				((MemoryPool*)memoryStack->AllocateRight(totalSize));
		if (!rst) {
			return 0;
		}
		rst->_chunkCount = chunkCount;
		rst->_chunkSize = chunkSize;
		rst->InitFreeChunk();
		return rst;
	}

}